#include "ConsoleDlg.h"
#include "ScriptLibrary.h"
#include <boost/foreach.hpp>
#include <vector>
#include <wx/Dir.h>
#include <wx/arrstr.h>
#include <fstream>
#include <wx/stdpaths.h>
#include <wx/filesys.h>
#include <tinyxmlstd.h>

ScriptLibrary::ScriptLibrary(gmMachine* m, ConsoleDlg* c)
	: mMachine(m)
	, mConsole(c)
	, mHasErrors(false)
{
}

bool ScriptLibrary::run(const string& file)
{
	if( compile(file) )
	{
		return true;
	}
	else return false;
}

bool ScriptLibrary::compile(const string& filename)
{
	std::ifstream file(filename.c_str());
	if (!file)
		return false;

	std::string fileString = std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
	file.close();

	const int errors = mMachine->ExecuteString(fileString.c_str());

	const bool gotErrors = mConsole->parseErrors(filename);
	const bool detectedErrors = errors > 0 || gotErrors;
	if( detectedErrors ) mHasErrors = true;
	return false==detectedErrors;
}

const bool ScriptLibrary::hasErrors() const
{
	return mHasErrors;
}

void ScriptLibrary::clearErrors()
{
	mHasErrors = false;
}

namespace // local
{
	wxDateTime GetModificationDateFor(const string& file)
	{
		wxFileSystem fs;
		wxFSFile* f = fs.OpenFile(file.c_str());
		if( f )
		{
			const wxDateTime d = f->GetModificationTime();
			delete f;
			return d;
		}
		else return wxDateTime::Now();
	}
}

bool ScriptLibrary::reload()
{
	bool allok = true;
	// foreach active script, run it
	BOOST_FOREACH(const EntryMap::value_type& p, entries)
	{
		const wxDateTime d = p.second;
		const string file = p.first;
		const wxDateTime modified = GetModificationDateFor(file);
		if( d < modified )
		{
			if( false == run(file, true) )
			{
				allok = false;
			}
		}
	}
	

	return allok;
}

bool ScriptLibrary::init()
{
	using namespace tinyxmlstd;
	Document doc;
	const wxString file((wxStandardPaths::Get().GetPluginsDir() + "/plugins/plugins.xml").c_str());
	if( false == LoadFile(&doc, WX_CSTR(file)) )
	{
		mConsole->addLog("failed loading: " + file);
		return false;
	}

	ElementPtr root = doc.getRootElement();

	bool allok = true;

	BOOST_FOREACH(Element& e, ElementsNamedIn(_T("plugin"), root->getChildren()))
	{
		const wxString name = e.getAttributes().get(_T("name"));
		if( false == loadManifest(WX_CSTR(name)) )
		{
			allok = false;
		}
	}

	return allok;
}

bool ScriptLibrary::loadManifest(const string& name)
{
	using namespace tinyxmlstd;
	Document doc;
	const wxString file((wxStandardPaths::Get().GetPluginsDir() + "/plugins/" + name + "/main.xml").c_str());
	if( false == LoadFile(&doc, WX_CSTR(file)) )
	{
		mConsole->addLog("failed loading manifest: " + file);
		return false;
	}

	ElementPtr root = doc.getRootElement();

	const bool dev = name[0] == '_';

	bool allok = true;

	BOOST_FOREACH(Element& e, ElementsNamedIn(_T("script"), root->getChildren()))
	{
		const wxString sname = e.getAttributes().get(_T("name"));
		const wxString sfile((wxStandardPaths::Get().GetPluginsDir() + "/plugins/" + name + "/" + sname ).c_str());
		if( false == run(WX_CSTR(sfile), dev) )
		{
			allok = false;
		}
	}
	return allok;
}

bool ScriptLibrary::load(const string& dir)
{
	// foreach new file in dir, run and addit it..
	wxArrayString files;
	wxDir::GetAllFiles(dir, &files, wxEmptyString, wxDIR_FILES);

	bool allok = true;
	for(size_t i = 0; i<files.GetCount(); ++i)
	{
		const wxString file = files[i];
		if( entries.find(WX_CSTR(file)) == entries.end() )
		{
			if( false == run(WX_CSTR(file)) )
			{
				allok = false;
			}
		}
	}

	return allok;
}

bool ScriptLibrary::run(const string& file, bool insert)
{
	const bool result = run(file.c_str());

	if( result ) mConsole->addLog(file + " has been sucessfully loaded");
	else mConsole->addLog(file + " failed to load");;

	if( insert )
	{
		entries[file.c_str()] = wxDateTime::Now();
	}

	return result;
}